package sh.grapendaal.tsuushin.module.uno;

import java.util.HashMap;
import java.util.Map;

import sh.grapendaal.tsuushin.core.Context;
import sh.grapendaal.tsuushin.message.MessageConstants.Color;
import sh.grapendaal.tsuushin.message.MessageConstants.Format;
import sh.grapendaal.tsuushin.message.NoticeMessage;
import sh.grapendaal.tsuushin.message.PrivateMessage;
import sh.grapendaal.tsuushin.module.uno.engine.Output;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Card;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Card.Rank;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Card.Suit;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Playable;
import sh.grapendaal.tsuushin.module.uno.engine.domain.Playable.Type;
import sh.grapendaal.tsuushin.module.uno.engine.domain.achievement.Achievement;
import sh.grapendaal.tsuushin.module.uno.engine.dto.PlayerState;
import sh.grapendaal.tsuushin.module.uno.engine.event.GameEvent;

public class IrcOutput extends Output {
	private static final Map<Suit, String> CARD_SUIT_MAP = new HashMap<Suit, String>();
	static {
		CARD_SUIT_MAP.put(Suit.WILDCARD, Format.INVERT + "W");
		CARD_SUIT_MAP.put(Suit.BLUE, Color.BLUE + "B");
		CARD_SUIT_MAP.put(Suit.GREEN, Color.GREEN + "G");
		CARD_SUIT_MAP.put(Suit.RED, Color.RED + "R");
		CARD_SUIT_MAP.put(Suit.YELLOW, Color.ORANGE + "Y");
	}

	private static final Map<Rank, String> CARD_RANK_MAP = new HashMap<Rank, String>();
	static {
		CARD_RANK_MAP.put(Rank.NONE, "");
		CARD_RANK_MAP.put(Rank.ZERO, "0");
		CARD_RANK_MAP.put(Rank.ONE, "1");
		CARD_RANK_MAP.put(Rank.TWO, "2");
		CARD_RANK_MAP.put(Rank.THREE, "3");
		CARD_RANK_MAP.put(Rank.FOUR, "4");
		CARD_RANK_MAP.put(Rank.FIVE, "5");
		CARD_RANK_MAP.put(Rank.SIX, "6");
		CARD_RANK_MAP.put(Rank.SEVEN, "7");
		CARD_RANK_MAP.put(Rank.EIGHT, "8");
		CARD_RANK_MAP.put(Rank.NINE, "9");
		CARD_RANK_MAP.put(Rank.DRAW_TWO, "D2");
		CARD_RANK_MAP.put(Rank.DRAW_FOUR, "D4");
		CARD_RANK_MAP.put(Rank.REVERSE, "R");
		CARD_RANK_MAP.put(Rank.SKIP, "S");
	}

	private Context context;
	private String destination;
	
	public void setContext(Context context) {
		this.context = context;
	}
	
	public void setDestination(String destination) {
		this.destination = destination;
	}

	private void sendPublicMessage(String message) {
		PrivateMessage outgoing = new PrivateMessage();
		outgoing.setDestination(this.destination);
		outgoing.setContent(message);
		this.context.getBot().send(outgoing);
	}

	private void sendPrivateMessage(String destination, String message) {
		NoticeMessage outgoing = new NoticeMessage();
		outgoing.setDestination(destination);
		outgoing.setContent(message);
		this.context.getBot().send(outgoing);
	}

	@Override
	public String formatPlayed(Playable played) {
		StringBuilder builder = new StringBuilder();
		builder.append(CARD_SUIT_MAP.get(played.getSuit()));
		builder.append(CARD_RANK_MAP.get(played.getRank()));
		builder.append(Format.RESET);

		if (played.getType() == Type.CARD) {
			Card playedCard = (Card) played;
			if (playedCard.getNextSuit() != null) {
				builder.append(" ");
				builder.append(CARD_SUIT_MAP.get(playedCard.getNextSuit()));
				builder.append(Format.RESET);
			}
		}

		return builder.toString();
	}

	@Override
	public void showAchievement(PlayerState playerState, Achievement achievement) {
		StringBuilder builder = new StringBuilder();
		builder.append(Color.GREEN).append("*** Achievement unlocked! ").append(Format.RESET);
		builder.append(Format.BOLD).append(achievement.getName()).append(Format.RESET);
		builder.append(" - ").append(achievement.getDescription());

		sendPrivateMessage(playerState.getNickname(), builder.toString());
	}

	@Override
	public void showCards(PlayerState playerState) {
		showCards(playerState, null);
	}

	private void showCards(PlayerState playerState, Card newCard) {
		StringBuilder builder = new StringBuilder();
		builder.append("Cards (");
		builder.append(playerState.getCards().size());
		builder.append("):");

		for (Card card : playerState.getCards()) {
			builder.append(" ").append(formatPlayed(card));
		}

		if (newCard != null) {
			builder.append(" | New: ");
			builder.append(formatPlayed(newCard));
		}

		sendPrivateMessage(playerState.getNickname(), builder.toString());
	}

	@Override
	public void showError(String message) {
		sendPublicMessage("Error: " + message);
	}

	@Override
	public void onPlayerJoin(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + " has joined the game.");
	}

	@Override
	public void onPlayerLeave(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + " has left the game.");
	}

	@Override
	public void onGameStart(GameEvent event) {
		sendPublicMessage("Uno has started with " + event.getPlayerState().getNickname() + "'s turn. Playing on "
				+ formatPlayed(event.getPlayed()) + ".");
	}

	@Override
	public void onGameStop(GameEvent event) {
		sendPublicMessage("Uno has been stopped by " + event.getPlayerState().getNickname() + ".");
	}

	@Override
	public void onCardPlay(GameEvent event) {
		// TODO Refactor
		if (event.getPlayed().getType() == Type.CARD) {
			Card played = (Card) event.getPlayed();

			if (played.getSuit() == Suit.WILDCARD) {
				if (played.getRank() == Rank.NONE) {
					sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played)
							+ ", changing the suit to " + CARD_SUIT_MAP.get(played.getNextSuit()) + Format.RESET + ".");
				} else {
					// WD4.
					sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played)
							+ " making " + event.getGameState().getNextPlayer().getNickname()
							+ " pick up 4 cards, and changed the suit to " + CARD_SUIT_MAP.get(played.getNextSuit())
							+ Format.RESET + ".");
				}
			} else if (played.getRank() == Rank.DRAW_TWO) {
				sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played) + " making "
						+ event.getGameState().getNextPlayer().getNickname() + " pick up 2 cards.");
			} else if (played.getRank() == Rank.REVERSE && event.getGameState().getPlayers().size() > 2) {
				sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played)
						+ " and reversed the order.");
			} else if (played.getRank() == Rank.SKIP
					|| (played.getRank() == Rank.REVERSE && event.getGameState().getPlayers().size() == 2)) {
				sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played)
						+ " and skipped " + event.getGameState().getNextPlayer().getNickname() + "\'s turn.");
			} else {
				sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(played) + ".");
			}
		} else {
			sendPublicMessage(event.getPlayerState().getNickname() + " played " + formatPlayed(event.getPlayed()) + ".");
		}
	}

	@Override
	public void onCardDraw(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + " drew a card.");
		showCards(event.getPlayerState(), (Card) event.getPlayed());
	}

	@Override
	public void onCardPass(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + " passed his/her turn.");
	}

	@Override
	public void onGameEnd(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + " won the game! "
				+ event.getPlayerState().getNickname() + "'s last hand was " + formatPlayed(event.getPlayed()) + ".");
	}

	@Override
	public void onTurnStart(GameEvent event) {
		sendPublicMessage(event.getPlayerState().getNickname() + "'s turn with "
				+ event.getPlayerState().getCards().size() + " cards. Playing on " + formatPlayed(event.getPlayed())
				+ ".");
		showCards(event.getPlayerState());
	}

	@Override
	public void onTurnSkip(GameEvent event) {

	}

	@Override
	public void onTurnEnd(GameEvent event) {

	}

	@Override
	public void onAchievementUnlock(GameEvent event, Achievement achievement) {
		showAchievement(event.getGameState().getCurrentPlayer(), achievement);
	}

}
